/*
 * Systemprogrammierung
 * Multiplayer-Quiz
 *
 * Gemeinsam verwendete Module
 *
 * rfc.c: Implementierung der Funktionen zum Senden und Empfangen von
 * Datenpaketen gemÃ¤ÃŸ dem RFC
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "rfc.h"
#include "server/user.h"
#include "server/clientthread.h"

#include <arpa/inet.h>

#include "common/util.h"
#include <errno.h>
#include <stddef.h>
#include <sys/select.h>
#include <netdb.h>
#include <stdint.h>

#pragma pack(1)
void CloseSocket(int fd)
{
        close(fd);
        debugPrint("Socket %d geschlossen", fd);
}

/*
 *      Diese Funktion erstellt einen Socket, welcher sich auf die übergebene Adresse verbindet
 */
int CreateClientSocket(char* ip, char* port)
{
		printf("hallo??");
        struct addrinfo *addr_info, *p;
        int ret, sock;

        ret = getaddrinfo(ip, port, NULL, &addr_info);

        if (ret)
        {
                printf("getaddrinfo: %s\n", gai_strerror(ret));
                exit(-1);
        }

        p = addr_info;

        sock = socket(AF_INET, SOCK_STREAM, 0);

        if (sock == -1)
        {
                perror("socket");
                return -1;
        }

        if (connect(sock, p->ai_addr, p->ai_addrlen) == 0)
        {
                freeaddrinfo(addr_info);
                printf("\nreturn test %d", sock);
                return sock;
        }
        freeaddrinfo(addr_info);
        return 0;
}


/**
 *              empfange eine RFC Nachricht
 **/
int ReceiveRFCMsg(int fd, struct NET_MESSAGE *buffer)
{
        char header[HEADER_LENGTH];

        errno = 0;
        int recvTotal;
        int recvret = recv(fd, header, HEADER_LENGTH, MSG_WAITALL);

        // es konnte kein Zeichen empfangen werden, vermutlich hat der Gegenstelle zugemacht
        if (recvret == 0)
        {
                // Socket wurde von der Gegenseite geschlossen
                if (errno == 0)
                {
                        debugPrint("Empfangen einer Nachricht fehlgeschlagen: Gegenstelle hat zu!");
                        return SOCKET_ERROR;
                }
                else
                {
                        debugPrint("Empfangen einer Nachricht fehlgeschlagen: ");
                        perror("");
                        return UNKNOWN_ERROR;
                }
        }

        if (recvret < 0)
        {
                debugPrint("Empfangen einer Nachricht fehlgeschlagen: ");
                perror("");
                return RECEIVE_ERROR;
        }

        // kumuliere empfangene Bytes
        recvTotal = recvret;

        // kopiere Header
        memmove(&buffer->header.type, &header[0], sizeof(uint8_t));
        memmove(&buffer->header.size, &header[1], sizeof(uint16_t));
        buffer->header.size = ntohs(buffer->header.size);

        // falls Nutzdaten vorhanden: allokiere Speicher und empfange Rest!
        if (buffer->header.size > 0)
        {
                errno = 0;

                // allokiere Speicher für empfangene Nutzdaten
                buffer->data = (void*) Uint8Malloc(buffer->header.size);

                recvret = recv(fd, buffer->data, buffer->header.size, MSG_WAITALL);

                // es konnte kein Zeichen empfangen werden, vermutlich hat der Gegenstelle zugemacht
                if (recvret == 0)
                {
                        // Socket wurde von der Gegenseite geschlossen
                        if (errno == 0)
                        {
                                debugPrint("Empfangen einer Nachricht fehlgeschlagen: Gegenstelle hat zu!");
                                return SOCKET_ERROR;
                        }
                        else
                        {
                                debugPrint("Empfangen einer Nachricht fehlgeschlagen: ");
                                perror("");
                                return UNKNOWN_ERROR;
                        }
                }

                if (recvret < 0)
                {
                        debugPrint("Empfangen einer Nachricht fehlgeschlagen: ");
                        perror("");
                        return RECEIVE_ERROR;
                }

                // kumuliere empfangene Bytes
                recvTotal += recvret;
        }
        // falls keine Nutzdaten vorhanden: allokiere keinen Speicher!
        else
        {
                // hiermit wird mitgeteilt: es wurde kein Speicher allokiert!
                buffer->data = NULL;
        }

        // finale Pausibilitätsprüfung
        if (recvTotal == HEADER_LENGTH + buffer->header.size)
                return recvTotal;
        else
        {
                debugPrint("Empfangen einer Nachricht fehlgeschlagen: Es konnten nicht alle Bytes empfangen werden");
                return UNKNOWN_ERROR;
        }
}

/*
 *      Diese Funktion erstellt eine gültige RFC Nachricht, die mit SendRFCMsg() versendet werden kann
 *
 *      Achtung!!! Der Speicher, auf den der data-Zeiger zeigt, darf nicht vor dem Versenden gefreed werden!
 */
struct NET_MESSAGE PackRFCMsg(char type, uint16_t size, void* data) {
        struct NET_MESSAGE msg;

        msg.header.type = type;
        msg.header.size = size;
        msg.data = data;

        return msg;
}

void SendRFCErrorMsg(int fd, char subtype, char* errormsg)
{
        struct NET_MESSAGE msg;
        msg.header.size = strlen(errormsg) + sizeof(subtype);
        msg.data = (char*) Uint8Malloc(msg.header.size);

        msg.header.type = TYPE_IS_ERROR;
        memmove(&msg.data[0], &subtype, sizeof(subtype));
        memmove(&msg.data[1], errormsg, strlen(errormsg));

        SendRFCMsg(fd, &msg);

        SecureFree(msg.data);
        msg.data = NULL;
}

/**
 *              sende eine RFC Nachricht
 **/
int SendRFCMsg(int fd, struct NET_MESSAGE *RFCmsg)
{
        uint16_t len = RFCmsg->header.size + HEADER_LENGTH;
        uint16_t swap;
        char *data;

        // drehe Endianess von size
        swap = swap_uint16(RFCmsg->header.size);

        // kopiere Daten wieder in Byta-Array
        data = (void*) Uint8Malloc(len);
        memmove(&data[MSG_TYPE_OFFSET], &RFCmsg->header.type, MSG_TYPE_LENGTH);
        memmove(&data[MSG_LENGTH_OFFSET], &swap, MSG_LENGTH_LENGTH);
        memmove(&data[DATA_OFFSET], RFCmsg->data, len - HEADER_LENGTH);

        int sendret = send(fd, data, len, 0);
        errno = 0;

        // es konnte kein Zeichen gesendet werden, vermutlich hat der Gegenstelle zugemacht
        if (sendret == 0)
        {
                // Socket wurde von der Gegenseite geschlossen
                if (errno == 0)
                {
                        debugPrint("Senden der Nachricht fehlgeschlagen: Gegenstelle hat zu!");
                        return SOCKET_ERROR;
                }
                // unbekannter Fehler
                else
                {
                        debugPrint("Senden der Nachricht fehlgeschlagen: ");
                        perror("");
                        return UNKNOWN_ERROR;
                }

        }
        // send() Fehler
        if (sendret < 0)
        {
                debugPrint("Senden der Nachricht fehlgeschlagen: ");
                perror("");
                return SEND_ERROR;
        }

        // gebe allokierten Speicher wieder frei
        SecureFree(data);
        data = NULL;

        // finale Pausibilitätsprüfung
        if (len == sendret)
                return sendret;
        else
        {
                debugPrint("Senden der Nachricht fehlgeschlagen: Es konnten nicht alle Bytes gesendet werden");
                return UNKNOWN_ERROR;
        }
}

//TODO Listener Socket erzeugen (

//TODO Client Verbindun akzeptieren

//TODO RFC nachricht empfangen

//TODO RFC Nachricht erstellen, die mit SendRFCMsg versendet wird

//TODO RFC Nachricht versenden

// Erstellt ein NET_MESSAGE struct aus einem Ã¼bergebenen Packet-Type und den zu sendenden Nutzdaten
struct NET_MESSAGE server_CreateMessage(uint8_t iType, char *sData, int iDataLen) {
	struct NET_HEADER oHeader;
	struct NET_MESSAGE oMessage;

	// Header

	oHeader.type = iType;
	oHeader.size = iDataLen;
	oMessage.header = oHeader;

	//infoPrint("\nCreating Package Type=%i | Laenge=%i | Inhalt=%s", iType, oHeader.size, sData);

	// Data
	oMessage.data = malloc(oHeader.size * sizeof(char));
	memmove(oMessage.data, sData, oHeader.size);

	return oMessage;
}



// Sendet eine NET_MESSAGE ueber ein angegebenes iSocket
// Returns SEND_ERROR bei Fehler und die Anzahl der gesendeten Bytes bei Erfolg
int server_SendServerMessage(int iSocket, struct NET_MESSAGE oMessage) {
	char sMessage[65535];

	memmove(sMessage, &oMessage.header.type, 1);
	memmove(sMessage + 1, oMessage.data, oMessage.header.size);

	infoPrint("\nSending Package Socket=%i | Type=%i | Laenge=%i | Inhalt=%s", iSocket, oMessage.header.type, oMessage.header.size + 1, oMessage.data);
	//hexdump(oMessage.data, oMessage.header.size, "data");

	if (write(iSocket, sMessage, (oMessage.header.size + 1)) == ((oMessage.header.size) + 1)) {
		return ((int)oMessage.header.size) + 1;
	}
	else {
		errorPrint("\nERROR while sending to client");
		return SEND_ERROR;
	}
}

int server_GetMessage(int iSocket, char *sMessage) {
	int iLength;
	char* sHeader = malloc(3 * sizeof(char));
	char* sRest;

	if (recv(iSocket, sHeader, 3, MSG_WAITALL) < 3) {
		// error
		return -1;
	}

	iLength = ((uint8_t)sHeader[1]) * 256 + ((uint8_t)sHeader[2]);
	infoPrint("\nRECEIVE: [0]=%d | Length=%d", (uint8_t)sHeader[0], iLength);
	hexdump(sHeader, 3, "data");

	if (iLength == 0) {
		memmove(sMessage, sHeader, 3);
	} else {
		sRest = malloc(iLength * sizeof(char));

		recv(iSocket, sRest, iLength, MSG_WAITALL);

		memmove(sMessage, sHeader, 3);
		memmove(sMessage + 3, sRest, iLength);
	}

	return iLength + 3;
}

int server_CheckMessageLength(char *sMessage) {
	return 0;
}

int server_SendErrorMessage(int iSocket, char* sMessage, int iCritical) {
	int iLen;
	char* sTemp;

	iLen = strlen(sMessage) + 1;

	sTemp = malloc((iLen + 2) * sizeof(char));

	sTemp[0] = (uint8_t)(iLen / 256);
	sTemp[1] = (uint8_t)iLen;

	if (iCritical == 1) {
		memmove(sTemp + 2, &iCritical, 1);
	} else {
		memmove(sTemp + 2, &iCritical, 1);
	}
	memmove(sTemp + 3, sMessage, iLen - 1);
	return server_SendServerMessage(iSocket, server_CreateMessage(TYPE_IS_ERROR, sTemp, iLen + 2));
}

#pragma pack(0)
